from common import sent_data, recv_data

from pathlib import Path
import json
import time
import socket
import random
import logging
import threading
import configparser


def search_port(conn: socket.socket):
    port = random.randint(20000, 60000)
    for _ in range(65535 - port):
        occp = conn.connect_ex(("127.0.0.1", port))
        if occp == 0:
            port += 1
        else:
            break
    else:
        raise OSError("No available port")
    return port


def recv_file(sub_server: socket.socket, file: Path, file_size: int):
    sub_server.listen(1)
    conn, addr = sub_server.accept()
    logging.info("connect to {}".format(addr))

    index = 0
    bufsize = cfg.getint("server", "buf")

    writer = file.open("wb+")
    logging.info("write to {}".format(file))

    while True:
        data = conn.recv(bufsize)
        if data:
            writer.write(data)
            index += len(data)
        else:
            time.sleep(0.001)

        if index >= file_size:
            break

    conn.close()
    writer.close()
    sub_server.close()


def message(conn: socket.socket):
    while True:
        js = recv_data(conn)
        if not js:
            continue
        js = json.loads(js)

        if js.get("close"):
            break
        elif js.get("file"):
            file: Path = base_path / js.get("file")
            size: int = js.get("size")

            port = search_port(conn)

            data = {"port": port}
            data = json.dumps(data)

            sent_data(conn, data)

            print("recv {} [{} kB] from {}".format(file, size, port))

            sub_server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            sub_server.bind(("", port))

            thread = threading.Thread(
                target=recv_file,
                args=(sub_server, file, size),
            )
            thread.start()
        else:
            conn.close()
            raise ValueError(js)

    conn.close()


def main():
    port = cfg.getint("server", "port")

    hostname = socket.gethostname()
    ip = socket.gethostbyname(hostname)
    print("ip", ip)

    # TCP connection
    server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server.bind(("", port))

    print("listening...")

    while True:
        server.listen(cfg.getint("server", "max_conn"))

        client_conn, client_addr = server.accept()
        print("connect to {}".format(client_addr))

        thread = threading.Thread(target=message, args=(client_conn,))
        thread.start()


if __name__ == "__main__":
    logging.basicConfig(filename="log", level=logging.INFO)

    cfg = configparser.ConfigParser()
    cfg.read("recv.ini")

    base_path = Path(cfg.get("server", "save"))

    main()
